package com.panopset.gui.container;

import static com.panopset.Util.EMPTY_STRING;
import static com.panopset.Util.generateStackHashKey;
import static com.panopset.Util.isPopulated;
import static com.panopset.Util.parentFrame;
import static com.panopset.gui.LookAndFeel.BG_COLOR;
import static com.panopset.gui.LookAndFeel.LINE_BORDER_COLOR_ALERT;
import static com.panopset.gui.LookAndFeel.LINE_BORDER_COLOR_INPROGRESS;
import static com.panopset.gui.LookAndFeel.LINE_BORDER_COLOR_NORMAL;
import static com.panopset.gui.LookAndFeel.LINE_BORDER_COLOR_WAIT;
import static com.panopset.gui.LookAndFeel.TITLE_FONT_NORMAL;

import java.awt.FlowLayout;
import java.awt.LayoutManager;
import java.util.Properties;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;

import com.panopset.AbstractApplication;
import com.panopset.Base64Coder;
import com.panopset.app.DocumentManager;
import com.panopset.gui.TPanel;

/**
 * Fields should:
 *
 * <ul><li>
 *   Have static access to font, color, other attributes
 *   that will be common to an application.
 * </li><li>
 *   Persist with a document, or application.
 * </li><li>
 *   Allow for validation.  (text, number, file, directory).
 * </li></ul>
 *
 * @author Karl Dinwiddie
 *
 */
public abstract class PanField<T extends JComponent> {

    protected final String defaultValue;
    private String value;
    private final String key;
    protected String borderTitle;
    private final String toolTipText;
    private LayoutManager layout;

    protected abstract String getComponentValue();

    protected abstract void setComponentValue(String value);

    public PanField(String borderTitle, String toolTipText, String defaultValue) {
        this(borderTitle, toolTipText, defaultValue, generateStackHashKey());
    }

    public PanField(String borderTitle, String toolTipText, String defaultValue, LayoutManager layout) {
        this(borderTitle, toolTipText, defaultValue, generateStackHashKey());
        this.layout = layout;
    }

    public PanField(String toolTipText, String defaultValue, LayoutManager layout) {
        this(null, toolTipText, defaultValue, generateStackHashKey());
        this.layout = layout;
    }

    public PanField(
            String borderTitle,
            String toolTipText,
            String defaultValue,
            String unqKey) {
        this.borderTitle = borderTitle;
        this.toolTipText = toolTipText;
        this.defaultValue = defaultValue == null ? EMPTY_STRING : defaultValue;
        this.key = unqKey;
        AbstractApplication.add(this);
    }

    public final String getValue() {
        if (value == null) {
            setValue(defaultValue);
        } else {
            setValue(getComponentValue());
        }
        return getComponentValue();
    }

    public void setValue(String value) {
        setComponentValue(value);
        this.value = value;
    }

    protected abstract T createComponent();

    protected void init() {
    }

    protected LayoutManager createLayout() {
        return new FlowLayout();
    }
    private TPanel panel;

    public JPanel getPanel() {
        if (panel == null) {
            panel = new TPanel.Builder().layout(getLayout()).construct();
            panel.setBackground(BG_COLOR);
            panel.setToolTipText(toolTipText);
            panel.add(getInnerComponent());
            setNormalHighlighting();
        }
        return panel;
    }
    private T gui;

    LayoutManager getLayout() {
        if (layout == null) {
            layout = createLayout();
        }
        return layout;
    }

    public T getInnerComponent() {
        if (gui == null) {
            gui = createComponent();
            gui.setToolTipText(toolTipText);
            init();
        }
        return gui;
    }

    public void load(Properties props) {
        String s = props.getProperty(this.key);
        setValue(isPopulated(s) ? Base64Coder.decodeString(s) : defaultValue);
    }

    public void flush(Properties props) {
        if (this.key == null) {
            throw new NullPointerException();
        }
        if (getValue() != null) {
            props.put(this.key, Base64Coder.encodeString(getValue()));
        }
    }
    protected TitledBorder normalBorder;
    protected TitledBorder waitBorder;
    protected TitledBorder alertBorder;
    protected TitledBorder inprogressBorder;

    public void setBorderTitle(String borderTitle) {
        this.borderTitle = borderTitle;
        getNormalBorder().setTitle(borderTitle);
        getWaitBorder().setTitle(borderTitle);
        getAlertBorder().setTitle(borderTitle);
        getInprogressBorder().setTitle(borderTitle);
    }

    protected TitledBorder getNormalBorder() {
        if (normalBorder == null) {
            LineBorder lb = new LineBorder(LINE_BORDER_COLOR_NORMAL, 1);
            normalBorder = new TitledBorder(lb, borderTitle);
            normalBorder.setTitleFont(TITLE_FONT_NORMAL);
            normalBorder.setTitleColor(LINE_BORDER_COLOR_NORMAL);
        }
        return normalBorder;
    }

    protected TitledBorder getWaitBorder() {
        if (waitBorder == null) {
            LineBorder lb = new LineBorder(LINE_BORDER_COLOR_WAIT, 1);
            waitBorder = new TitledBorder(lb, borderTitle);
            waitBorder.setTitleColor(LINE_BORDER_COLOR_WAIT);
            waitBorder.setTitleFont(TITLE_FONT_NORMAL);
        }
        return waitBorder;
    }

    protected TitledBorder getAlertBorder() {
        if (alertBorder == null) {
            LineBorder lb = new LineBorder(LINE_BORDER_COLOR_ALERT, 1);
            alertBorder = new TitledBorder(lb, borderTitle);
            alertBorder.setTitleColor(LINE_BORDER_COLOR_ALERT);
            alertBorder.setTitleFont(TITLE_FONT_NORMAL);
            setNormalHighlighting();
        }
        return alertBorder;
    }

    protected TitledBorder getInprogressBorder() {
        if (inprogressBorder == null) {
            LineBorder lb = new LineBorder(LINE_BORDER_COLOR_INPROGRESS, 1);
            inprogressBorder = new TitledBorder(lb, borderTitle);
            inprogressBorder.setTitleColor(LINE_BORDER_COLOR_INPROGRESS);
            inprogressBorder.setTitleFont(TITLE_FONT_NORMAL);
            setNormalHighlighting();
        }
        return inprogressBorder;
    }

    public void setNormalHighlighting() {
        if (borderTitle == null) {
            return;
        }
        getPanel().setBorder(getNormalBorder());
        reset();
    }

    /**
     * If you want to show something in progress, use this.
     */
    public void setWaitHighlighting() {
        if (borderTitle == null) {
            return;
        }
        getPanel().setBorder(getWaitBorder());
        reset();
    }

    public void setAlertHighlighting() {
        if (borderTitle == null) {
            return;
        }
        getPanel().setBorder(getAlertBorder());
        reset();
    }

    public void setInprogressHighlighting() {
        if (borderTitle == null) {
            return;
        }
        getPanel().setBorder(getInprogressBorder());
        reset();
    }

    public void reset() {
        getPanel().invalidate();
        if (parentFrame != null) {
            parentFrame.getFrame().validate();
        }
    }

    /**
     * Override to do anything after loading.
     */
    public void doneLoading() {
    }
}
